// index.js
/**
 * 文件传输服务端
 * 基于Node.js实现
 * 支持大文件分段传输，按照特定通信格式解析数据
 * 
 * 通信格式：
 * 1. 文件夹头：
 *    - 1字节：协议版本号(0x01)
 *    - 2字节：文件夹名长度(大端序)
 *    - N字节：文件夹名(UTF8编码)
 *    - 8字节：文件总数(大端序)
 * 
 * 2. 文件块：
 *    - 1字节：文件标识(0x02)
 *    - 2字节：相对路径长度(大端序)
 *    - N字节：相对路径(UTF8编码)
 *    - 8字节：文件大小(大端序)
 *    - M字节：文件内容
 */

const net = require('net');
const fs = require('fs');
const path = require('path');

// 基础配置
const PORT = 3000;
const BASE_DIR = path.resolve('./received_files');

// 确保基础目录存在
if (!fs.existsSync(BASE_DIR)) {
    fs.mkdirSync(BASE_DIR, { recursive: true });
}

class BufferManager {
    constructor() {
        this.buffer = Buffer.alloc(0);
    }
    
    get length() {
        return this.buffer.length;
    }
    
    append(data) {
        this.buffer = Buffer.concat([this.buffer, data]);
    }
    
    consume(length) {
        if (length > 0 && length <= this.buffer.length) {
            this.buffer = this.buffer.slice(length);
        }
    }
    
    peek(length) {
        if (this.buffer.length < length) return null;
        return this.buffer.slice(0, length);
    }
    
    read(length) {
        const data = this.peek(length);
        if (data) {
            this.consume(length);
        }
        return data;
    }
    
    // 新增：预览读取方法，不消费数据
    peekUInt8(offset = 0) {
        if (this.buffer.length < offset + 1) return null;
        return this.buffer[offset];
    }
    
    peekUInt16BE(offset = 0) {
        if (this.buffer.length < offset + 2) return null;
        return this.buffer.readUInt16BE(offset);
    }
    
    peekBigUInt64BE(offset = 0) {
        if (this.buffer.length < offset + 8) return null;
        return this.buffer.readBigUInt64BE(offset);
    }
    
    peekString(offset, length) {
        if (this.buffer.length < offset + length) return null;
        return this.buffer.slice(offset, offset + length).toString('utf8');
    }
    
    readUInt8() {
        const data = this.read(1);
        return data ? data[0] : null;
    }
    
    readUInt16BE() {
        const data = this.read(2);
        return data ? data.readUInt16BE(0) : null;
    }
    
    readBigUInt64BE() {
        const data = this.read(8);
        return data ? data.readBigUInt64BE(0) : null;
    }
    
    readString(length) {
        const data = this.read(length);
        return data ? data.toString('utf8') : null;
    }
}

class ProtocolParser {
    constructor(baseDir, connectionId) {
        this.state = 'HEADER';
        this.buffer = new BufferManager();
        this.currentFile = null;
        this.remainingFiles = 0;
        this.baseDir = baseDir;
        this.folderName = '';
        this.connectionId = connectionId;
        console.log(`[${connectionId}] 新连接建立`);
    }
    
    process(data) {
        this.buffer.append(data);
        //console.log(`[${this.connectionId}] 接收到${data.length}字节数据，当前缓冲区大小: ${this.buffer.length}字节`);
        
        let processed = true;
        while (processed) {
            processed = false;
            
            switch (this.state) {
                case 'HEADER':
                    processed = this.processHeader();
                    break;
                case 'META':
                    processed = this.processMeta();
                    break;
                case 'FILE':
                    processed = this.processFile();
                    break;
            }
        }
    }
    
    processHeader() {
        const version = this.buffer.readUInt8();
        if (version === null) return false;
        
        if (version !== 0x01) {
            throw new Error(`[${this.connectionId}] 协议版本错误: ${version}`);
        }
        
        console.log(`[${this.connectionId}] 协议版本: ${version}`);
        this.state = 'META';
        return true;
    }
    
    processMeta() {
        const nameLen = this.buffer.readUInt16BE();
        if (nameLen === null) return false;
        
        const folderName = this.buffer.readString(nameLen);
        if (folderName === null) return false;
        
        const fileCount = this.buffer.readBigUInt64BE();
        if (fileCount === null) return false;
        
        this.folderName = folderName;
        this.remainingFiles = Number(fileCount);
        
        console.log(`[${this.connectionId}] 文件夹: ${folderName}, 文件总数: ${this.remainingFiles}`);
        
        this.createFolder(folderName);
        
        this.state = 'FILE';
        return true;
    }
    
    processFile() {
        if (!this.currentFile) {
            // 先预览检查数据是否足够
            if (this.buffer.length < 1) return false; // 至少需要1字节标识符
            
            const mark = this.buffer.peekUInt8(0);
            if (mark !== 0x02) {
                throw new Error(`[${this.connectionId}] 文件标识错误: ${mark}`);
            }
            
            if (this.buffer.length < 3) return false; // 需要1字节标识 + 2字节路径长度
            
            const pathLen = this.buffer.peekUInt16BE(1);
            const totalHeaderSize = 1 + 2 + pathLen + 8; // 标识(1) + 路径长度(2) + 路径内容(pathLen) + 文件大小(8)
            
            if (this.buffer.length < totalHeaderSize) {
                return false; // 等待更多数据
            }
            
            // 现在安全地读取所有数据
            const actualMark = this.buffer.readUInt8();
            const actualPathLen = this.buffer.readUInt16BE();
            const filePath = this.buffer.readString(actualPathLen);
            const fileSize = this.buffer.readBigUInt64BE();
            
            this.currentFile = {
                path: filePath,
                size: Number(fileSize),
                received: 0
            };
            
            console.log(`[${this.connectionId}] 开始接收文件: ${filePath}, 大小: ${fileSize}字节`);
            
            this.createFile(this.currentFile);
            
            return true;
        }
        
        const remain = this.currentFile.size - this.currentFile.received;
        if (remain <= 0) {
            this.closeCurrentFile();
            return true;
        }
        
        const readSize = Math.min(remain, this.buffer.length);
        if (readSize <= 0) return false;
        
        const chunk = this.buffer.read(readSize);
        if (!chunk) return false;
        
        if (this.currentFile.writeStream) {
            this.currentFile.writeStream.write(chunk);
            this.currentFile.received += chunk.length;
            
            const progress = Math.floor((this.currentFile.received / this.currentFile.size) * 100);
            if (progress % 10 === 0 || this.currentFile.received === this.currentFile.size) {
                //console.log(`[${this.connectionId}] 文件: ${this.currentFile.path} 进度: ${progress}%`);
            }
            
            if (this.currentFile.received >= this.currentFile.size) {
                this.closeCurrentFile();
            }
        }
        
        return true;
    }
    
    closeCurrentFile() {
        if (this.currentFile && this.currentFile.writeStream) {
            this.currentFile.writeStream.end();
            console.log(`[${this.connectionId}] 文件接收完成: ${this.currentFile.path}, 大小: ${this.currentFile.received}字节`);
            
            if (this.currentFile.received !== this.currentFile.size) {
                console.error(`[${this.connectionId}] 文件大小不匹配: ${this.currentFile.path}, 预期: ${this.currentFile.size}, 实际: ${this.currentFile.received}`);
            }
            
            this.remainingFiles--;
            console.log(`[${this.connectionId}] 剩余文件数: ${this.remainingFiles}`);
            
            if (this.remainingFiles <= 0) {
                console.log(`[${this.connectionId}] 所有文件接收完成`);
            }
            
            this.currentFile = null;
        }
    }
    
    createFolder(folderName) {
        const targetDir = path.join(this.baseDir, folderName);
        
        const safePath = path.resolve(targetDir);
        if (!safePath.startsWith(this.baseDir)) {
            throw new Error(`[${this.connectionId}] 路径越界攻击尝试: ${targetDir}`);
        }
        
        if (!fs.existsSync(targetDir)) {
            console.log(`[${this.connectionId}] 创建目录1: ${targetDir}`);
            fs.mkdirSync(targetDir, { recursive: true });
        }
    }
    
    createFile(fileMeta) {
        const fullPath = path.join(this.baseDir, this.folderName, fileMeta.path);
        
        const safePath = path.resolve(fullPath);
        if (!safePath.startsWith(this.baseDir)) {
            throw new Error(`[${this.connectionId}] 路径越界攻击尝试: ${fullPath}`);
        }
        
        const dirPath = path.dirname(fullPath);
        if (!fs.existsSync(dirPath)) {
            console.log(`[${this.connectionId}] 创建目录2: ${dirPath}`);
            fs.mkdirSync(dirPath, { recursive: true });
        }
        
        fileMeta.writeStream = fs.createWriteStream(fullPath);
        console.log(`[${this.connectionId}] 创建文件: ${fullPath}`);
        
        fileMeta.writeStream.on('error', (err) => {
            console.error(`[${this.connectionId}] 文件写入错误: ${fullPath}`, err);
        });
    }
    
    close() {
        if (this.currentFile && this.currentFile.writeStream) {
            this.currentFile.writeStream.end();
        }
        console.log(`[${this.connectionId}] 连接关闭`);
    }
}

// 创建TCP服务器
const server = net.createServer((socket) => {
    const connectionId = `conn_${Date.now()}_${Math.floor(Math.random() * 1000)}`;
    
    const parser = new ProtocolParser(BASE_DIR, connectionId);
    
    socket.setNoDelay(true);
    
    socket.on('data', (data) => {
        try {
            parser.process(data);
        } catch (err) {
            console.error(`[${connectionId}] 处理数据错误:`, err);
            socket.end();
        }
    });
    
    socket.on('close', () => {
        parser.close();
    });
    
    socket.on('error', (err) => {
        console.error(`[${connectionId}] 连接错误:`, err);
        parser.close();
    });
    
    socket.setTimeout(30000);
    socket.on('timeout', () => {
        console.warn(`[${connectionId}] 连接超时`);
        socket.end();
    });
});

server.listen(PORT, () => {
    console.log(`文件传输服务器已启动，监听端口: ${PORT}`);
    console.log(`文件将保存到: ${BASE_DIR}`);
});

server.on('error', (err) => {
    console.error('服务器错误:', err);
});

process.on('SIGINT', () => {
    console.log('正在关闭服务器...');
    server.close(() => {
        console.log('服务器已关闭');
        process.exit(0);
    });
});